package cn.com.hatech.cdkey.module.generate.service.impl;

import cn.com.hatech.cdkey.common.exception.TipException;
import cn.com.hatech.cdkey.common.utils.AESOperator;
import cn.com.hatech.cdkey.config.upload.UploadConfig;
import cn.com.hatech.cdkey.module.generate.entity.CdKey;
import cn.com.hatech.cdkey.module.generate.mapper.ICdKeyMapper;
import cn.com.hatech.cdkey.module.generate.service.ICdKeyService;
import cn.com.hatech.cdkey.module.generate.vo.DownLoadFile;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * @author xuruikai
 */
@Service
public class CdKeyServiceImpl extends ServiceImpl<ICdKeyMapper, CdKey> implements ICdKeyService {

    @Resource
    private ICdKeyMapper cdKeyMapper;
    /**
     * 增加混淆的公司描述信息
     */
    private static final String info = "北京同创永益科技发展有限公司（以下简称“同创永益”）是面向未来的组织韧性服务提供商，" +
            "专注于提供业务连续性、IT韧性和灾难恢复相关产品、解决方案及服务的国家级高新技术企业。公司在业务连续性、灾难恢复、IT运营管理、IT容量韧性、3D可视化、" +
            "风险与应急管理等专业领域为企业级客户提供完整的解决方案，并通过与众多优秀合作伙伴搭建完善的产业生态体系，共同为企业级客户组织韧性保驾护航。作为该领域的" +
            "领军企业，公司凭借领先的技术创新实力、强大的本地服务能力和个性化交付能力得到了资本市场的认可。2019年8月，同创永益完成近亿元B轮融资，步入发展的快车道。  " +
            "目前，同创永益已经成功服务于金融、能源、制造、电信等行业的众多头部客户，累计近百家全球及中国500强企业，其中包括国家电网、南方电网、中国移动、中石化、" +
            "中国海油、万达集团、万华化学、中国银联、上海黄金交易所、江苏省农信、湖北省农信、武汉农村商业银行、南京银行、太平保险集团等。  " +
            "同创永益总部设在北京，在上海、深圳、武汉、南京、苏州、成都、郑州等地均设有分公司和办事处，构建了包括高端咨询服务、" +
            "软件现场交付服务、400售后服务于一体的完整服务体系，打造行业最佳落地实践和客户体验。目前公司人员80%以上为研发和咨询团队，" +
            "核心成员均是来自于IBM、EMC、华为、工商银行等具备多年中大型企业客户服务经验的专家。  公司秉承开放共赢的理念，不断自主研发创新，期望在业务连续性、" +
            "IT韧性和灾难恢复领域成为最具影响力的领导者，成为客户业务连续性的最后一道防线，并携手合作伙伴，在支撑客户关键业务成长的同时，为客户持续创造新的业务价值，" +
            "围绕客户需求和产业发展，共创美好数字化未来。";

    @Override
    public String generateCode(CdKey cdKey) throws Exception {
        if (StringUtils.isEmpty(cdKey.getUsername())) {
            throw new TipException("租户管理员账号不能为空");
        }
        if (cdKey.getNewAdd() == 0) {
            LambdaQueryWrapper<CdKey> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(CdKey::getUsername, cdKey.getUsername());
            if (cdKeyMapper.selectCount(lambdaQueryWrapper) > 0) {
                throw new TipException("已生成过此租户激活码, 请操作激活码追加功能");
            }
        }
        // 设置菜单
        cdKey.setMenuCodes(this.getMenuCodes(cdKey.getMenuCodes()));
        // 混淆编码
        cdKey.setConfusion(IdUtil.randomUUID());
        // 描述信息
        cdKey.setDescription(info);
        // license加密
        String licenseCode = AESOperator.getInstance().encrypt(JSONObject.toJSONString(cdKey), AESOperator.tenantLicenseKey);
        cdKey.setLicenseCode(licenseCode);
        cdKey.setMenuCode(JSONObject.toJSONString(cdKey.getMenuCodes()));
        // 生成时间
        cdKey.setCreateTime(new Date());
        cdKeyMapper.insert(cdKey);
        return licenseCode;
    }

    @Override
    public List<String> selectAllTenant() {
        Set set = cdKeyMapper.selectList(null).stream().map(CdKey::getUsername).collect(Collectors.toSet());
        return new ArrayList<>(set);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchGenerate(HttpServletResponse response, CdKey cdKey) throws Exception {
        List<CdKey> cdKeys = new ArrayList<>();
        for (String username : cdKey.getUsernames()) {
            CdKey key = new CdKey();
            key.setUsername(username);
            key.setUserCount(cdKey.getUserCount());
            key.setBusinessCount(cdKey.getBusinessCount());
            key.setAssetsCount(cdKey.getAssetsCount());
            key.setDaysUsed(cdKey.getDaysUsed());
            key.setMenuCodes(cdKey.getMenuCodes());
            key.setUseScreen(cdKey.getUseScreen());
            key.setNewAdd(cdKey.getNewAdd());
            key.setLicenseCode(generateCode(key));
            cdKeys.add(key);
        }
        downloadFiles(response, cdKeys);
    }

    public void downloadFiles(HttpServletResponse response, List<CdKey> cdKeys) {
        try {
            // 设置编码字符
            response.setContentType("text/html; charset=UTF-8");
            // 设置内容类型为下载类型
            response.setContentType("application/x-msdownload");
            // 设置下载的文件名称
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("激活码.zip", "UTF-8"));
            //创建压缩文件需要的空的zip包 ，这里是自动生成的，不用我们自己去生成
            // 根据临时的zip压缩包路径，创建zip文件
            //创建压缩文件需要的空的zip包 ，这里是自动生成的，不用我们自己去生成
            String zipFilePath = UploadConfig.getUploadPath() + "激活码.zip";
            File zip = new File(zipFilePath);
            if (!zip.exists()) {
                zip.createNewFile();
            }
            // 创建zip文件输出流
            FileOutputStream fos = new FileOutputStream(zip);
            ZipOutputStream zos = new ZipOutputStream(fos);
            // 获取下载文件
            List<DownLoadFile> fileList = getFileList(cdKeys);
            //循环读取文件路径集合，获取每一个文件的路径（将文件一个一个进行压缩）
            for (DownLoadFile file : fileList) {
                // 将每一个文件写入zip文件包内，即进行打包
                zipFile(file, zos);
            }
            zos.close();
            OutputStream out = response.getOutputStream();
            InputStream fis = new BufferedInputStream(new FileInputStream(zipFilePath));
            byte[] buff = new byte[4096];
            int size = 0;
            while ((size = fis.read(buff)) != -1) {
                out.write(buff, 0, size);
            }
            //清除缓存
            out.flush();
            out.close();
            fis.close();
            fos.close();
            zos.close();
        } catch (FileNotFoundException e) {
            log.error("下载异常", e);
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("下载异常", e);
        }
    }

    /**
     * 根据勾选的ids获取文件
     *
     * @param cdKeys
     * @return
     */
    public List<DownLoadFile> getFileList(List<CdKey> cdKeys) {
        List<DownLoadFile> fileList = new ArrayList<>();
        if (cdKeys != null) {
            //传入id不为空,获取传入的id文件路径
            cdKeys.forEach(key -> {
                String fileName = writeTXT(key.getUsername() + "激活码", key.getLicenseCode());
                if (!StringUtils.isEmpty(fileName)) {
                    fileList.add(new DownLoadFile(fileName, key.getUsername() + "激活码.txt"));
                }
            });
        }
        return fileList;
    }

    public static String writeTXT(String title, String content) {
        try {
            // 相对路径，如果没有则要建立一个新的output。txt文件
            File writename = new File(ResourceUtils.getURL("classpath:").getPath());
            if (!writename.exists()) {
                writename.mkdirs();
            }
            // 相对路径，如果没有则要建立一个新的txt文件
            writename = new File(ResourceUtils.getURL("classpath:").getPath() + "\\" + title + ".txt");
            writename.createNewFile(); // 创建新文件
            BufferedWriter out = new BufferedWriter(new FileWriter(writename));
            out.write(content);
            // 把缓存区内容压入文件
            out.flush();
            // 最后记得关闭文件
            out.close();
            return ResourceUtils.getURL("classpath:").getPath() + "\\" + title + ".txt";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件压缩
     *
     * @param inputFile
     * @param zipoutputStream
     */
    private void zipFile(DownLoadFile inputFile, ZipOutputStream zipoutputStream) {
        try (FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis)) {
            // 判断文件是否存在
            if (inputFile.exists()) {
                // 判断是否属于文件，还是文件夹
                if (inputFile.isFile()) {
                    // 创建输入流读取文件
                    // 将文件写入zip内，即将文件进行打包
                    // 获取文件名
                    ZipEntry ze = new ZipEntry(inputFile.getShowName());
                    zipoutputStream.putNextEntry(ze);
                    // 写入文件的方法，同上
                    byte[] b = new byte[1024];
                    long l = 0;
                    while (l < inputFile.length()) {
                        int j = bis.read(b, 0, 1024);
                        l += j;
                        zipoutputStream.write(b, 0, j);
                    }
                }
            }
        } catch (Exception e) {
            log.error("文件不存在", e);
            e.printStackTrace();
        }
    }


    private List<String> getMenuCodes(List<String> menus) {
        List<String> menuCodes = new ArrayList<>();
        // 系统管理
        menuCodes.add("bcms_1");
        // 基础资源
        menuCodes.add("bcms_2");
        // 业务影响分析服务
        if (menus.contains("1")) {
            // 业务影响分析
            menuCodes.add("bcms_11");
        }
        // 风险分析服务
        if (menus.contains("2")) {
            // 风险分析
            menuCodes.add("bcms_12");
        }
        // 预案管理服务
        if (menus.contains("3")) {
            // 改进管理
            menuCodes.add("bcms_6");
            // 预案管理
            menuCodes.add("bcms_7");
            // 体系管理
            menuCodes.add("bcms_14");
        }
        // 演练服务
        if (menus.contains("4")) {
            // 演练管理
            menuCodes.add("bcms_4");
            // 改进管理
            menuCodes.add("bcms_6");
            // 体系管理
            menuCodes.add("bcms_14");
        }
        // 应急服务
        if (menus.contains("5")) {
            // 监管报送
            menuCodes.add("bcms_17");
            // 应急管理
            menuCodes.add("bcms_18");
        }
        return menuCodes;
    }
}
